<?php

class JenaTextSparql extends GenericSparql {

  /**
   * How many results to ask from the jena-text index. jena-text defaults to
   * 10000, but that is too little in some cases (see Issue 109).
   */
  private $MAX_N = 100000000;

  /**
   * Query for concepts using a search term. Use the jena-text Lucene index when possible.
   * @param string $term search term
   * @param string $lang language code
   * @param int $limit maximum number of hits to retrieve; 0 for unlimited
   * @param int $offset offset of results to retrieve; 0 for beginning of list
   * @param string $type limit search to concepts of the given type
   * @param string $parent limit search to concepts which have the given concept as parent in the transitive broader hierarchy
   * @param string $group limit search to concepts which are in the given group
   * @return EasyRDF_Sparql_Result query result object
   */

  public function queryConcepts($term, $lang, $limit, $offset, $type, $parent=null, $group=null) {
    $gc = $this->graphClause;
    $gr = $this->graph[0] == '?' ? $this->graph : '';
    $limit = ($limit) ? 'LIMIT ' . $limit : '';
    $offset = ($offset) ? 'OFFSET ' . $offset : '';
    $type = EasyRdf_Namespace::expand($type);

    // extra conditions for label language, if specified
    $labelcond = ($lang) ? "&& langMatches(lang(?label), '$lang')" : "";
    $labelcond2 = ($lang) ? "&& langMatches(lang(?alabel), '$lang')" : "";
    
    // extra conditions for parent and group, if specified
    $parentcond = ($parent) ? "?s skos:broader+ <$parent> ." : "";
    $groupcond = ($group) ? "<$group> skos:member ?s ." : "";

    $orderextra = ($this->graph[0] == '?') ? $this->graph : '';

    if ($term == '0-9') {
      $term = '[0-9]';
    } elseif ($term == '!*') {
      $term = '[^A-Za-zåÅöÖäÄ0-9]';
    }
    
    while (strpos($term, '**') !== false)
      $term = str_replace('**', '*', $term); // removes futile asterisks

    // construct the lucene search term for jena-text
    $textterm = $term;
    $textterm = str_replace('-', ' ', $textterm); // split words with hyphens to separate words
    $textterm = str_replace(':', ' ', $textterm); // split words with colons to separate words
    $textterm = str_replace('/', ' ', $textterm); // split words with slashes to separate words
    $textterm = str_replace('(', ' ', $textterm); // split words with parentheses to separate words
    $textterm = str_replace(')', ' ', $textterm); // split words with parentheses to separate words
    $textterm = str_replace('\'', '\\\'', $textterm); // ensure single quotes are quoted
    if (preg_match('/^\p{L}[\p{L} _.-]+\*?$/u', $textterm) != 1) $textterm = ''; // avoid problematic chars - disable text index
    if ($textterm != trim($textterm)) $textterm = ''; // avoid whitespace problems - disable text index
    $textcond = ($textterm != '' ? "{ ?s text:query ('$textterm' $this->MAX_N) }" : '');

    # make sure regex metacharacters are not passed through
    $term = str_replace('\\', '\\\\', preg_quote($term));
    $term = str_replace('\\\\*', '.*', $term); // just to fix regex-syntax to work as expected
    $term = str_replace('\'', '\\\'', $term); // ensure single quotes are quoted

    $query = <<<EOQ
      SELECT DISTINCT ?s ?label ?alabel $gr WHERE {
       $textcond
       $gc {
        {
         {
          ?s skos:prefLabel ?label .
          BIND (?label as ?match)
          ?s rdf:type <$type> .
          $parentcond
          $groupcond
          FILTER (regex(str(?label), '^$term$', "i")
                  $labelcond)
         }
         UNION
         {
          ?s skos:altLabel ?alabel .
          BIND (?alabel as ?match)
          ?s rdf:type <$type> .
          $parentcond
          $groupcond
          ?s skos:prefLabel ?label .
          FILTER (regex(str(?alabel), '^$term$', "i")
                  && !regex(str(?label), '^$term$', "i")
                  && langMatches(lang(?alabel), lang(?label))
                  $labelcond2
                 )
         }
         FILTER NOT EXISTS {?s owl:deprecated true }
        }
       }

      } ORDER BY lcase(?match) $orderextra
      $limit
      $offset
EOQ;
    return $this->client->query($query);
  }
}

?>
